package com.wb.mianshimiao.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wb.mianshimiao.common.ErrorCode;
import com.wb.mianshimiao.constant.CommonConstant;
import com.wb.mianshimiao.exception.BusinessException;
import com.wb.mianshimiao.exception.ThrowUtils;
import com.wb.mianshimiao.mapper.QuestionBankQuestionMapper;
import com.wb.mianshimiao.model.dto.questionbankquestion.QuestionBankQuestionQueryRequest;
import com.wb.mianshimiao.model.entity.Question;
import com.wb.mianshimiao.model.entity.QuestionBank;
import com.wb.mianshimiao.model.entity.QuestionBankQuestion;
import com.wb.mianshimiao.model.entity.User;
import com.wb.mianshimiao.model.vo.QuestionBankQuestionVO;
import com.wb.mianshimiao.model.vo.UserVO;
import com.wb.mianshimiao.service.QuestionBankQuestionService;
import com.wb.mianshimiao.service.QuestionBankService;
import com.wb.mianshimiao.service.QuestionService;
import com.wb.mianshimiao.service.UserService;
import com.wb.mianshimiao.utils.SqlUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 题库题目关联服务实现
 *

 */
@Service
@Slf4j
public class QuestionBankQuestionServiceImpl extends ServiceImpl<QuestionBankQuestionMapper, QuestionBankQuestion> implements QuestionBankQuestionService {

    @Resource
    private UserService userService;

    @Resource
    private QuestionBankService questionBankService;

    @Resource
    @Lazy
    private QuestionService questionService;

    @Resource
    private QuestionBankQuestionMapper questionBankQuestionMapper;

    /**
     * 校验数据
     *
     * @param questionBankquestion
     * @param add      对创建的数据进行校验
     */
    @Override
    public void validQuestionBankQuestion(QuestionBankQuestion questionBankquestion, boolean add) {
        ThrowUtils.throwIf(questionBankquestion == null, ErrorCode.PARAMS_ERROR);
        // 题目和题库必须存在
        Long questionId = questionBankquestion.getQuestionId();
        if (questionId != null) {
            Question question = questionService.getById(questionId);
            ThrowUtils.throwIf(question == null, ErrorCode.NOT_FOUND_ERROR, "题目不存在");
        }
        Long questionBankId = questionBankquestion.getQuestionBankId();
        if (questionBankId != null) {
            QuestionBank questionBank = questionBankService.getById(questionBankId);
            ThrowUtils.throwIf(questionBank == null, ErrorCode.NOT_FOUND_ERROR, "题库不存在");
        }
    }

    /**
     * 获取查询条件
     *
     * @param questionBankquestionQueryRequest
     * @return
     */
    @Override
    public QueryWrapper<QuestionBankQuestion> getQueryWrapper(QuestionBankQuestionQueryRequest questionBankquestionQueryRequest) {
        QueryWrapper<QuestionBankQuestion> queryWrapper = new QueryWrapper<>();
        if (questionBankquestionQueryRequest == null) {
            return queryWrapper;
        }
        // todo 从对象中取值
        Long id = questionBankquestionQueryRequest.getId();
        Long notId = questionBankquestionQueryRequest.getNotId();
        String sortField = questionBankquestionQueryRequest.getSortField();
        String sortOrder = questionBankquestionQueryRequest.getSortOrder();
        Long userId = questionBankquestionQueryRequest.getUserId();
        Long questionBankId = questionBankquestionQueryRequest.getQuestionBankId();
        Long questionId = questionBankquestionQueryRequest.getQuestionId();
        // todo 补充需要的查询条件
        // 精确查询
        queryWrapper.ne(ObjectUtils.isNotEmpty(notId), "id", notId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(id), "id", id);
        queryWrapper.eq(ObjectUtils.isNotEmpty(userId), "userId", userId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(questionBankId), "questionBankId", questionBankId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(questionId), "questionId", questionId);
        // 排序规则
        queryWrapper.orderBy(SqlUtils.validSortField(sortField),
                sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);
        return queryWrapper;
    }

    /**
     * 获取题库题目关联封装
     *
     * @param questionBankquestion
     * @param request
     * @return
     */
    @Override
    public QuestionBankQuestionVO getQuestionBankQuestionVO(QuestionBankQuestion questionBankquestion, HttpServletRequest request) {
        // 对象转封装类
        QuestionBankQuestionVO questionBankquestionVO = QuestionBankQuestionVO.objToVo(questionBankquestion);

        // todo 可以根据需要为封装对象补充值，不需要的内容可以删除
        // region 可选
        // 1. 关联查询用户信息
        Long userId = questionBankquestion.getUserId();
        User user = null;
        if (userId != null && userId > 0) {
            user = userService.getById(userId);
        }
        UserVO userVO = userService.getUserVO(user);
        questionBankquestionVO.setUser(userVO);
        // 2. 已登录，获取用户点赞、收藏状态
        long questionBankquestionId = questionBankquestion.getId();
        User loginUser = userService.getLoginUserPermitNull(request);
        // endregion

        return questionBankquestionVO;
    }

    /**
     * 分页获取题库题目关联封装
     *
     * @param questionBankquestionPage
     * @param request
     * @return
     */
    @Override
    public Page<QuestionBankQuestionVO> getQuestionBankQuestionVOPage(Page<QuestionBankQuestion> questionBankquestionPage, HttpServletRequest request) {
        List<QuestionBankQuestion> questionBankquestionList = questionBankquestionPage.getRecords();
        Page<QuestionBankQuestionVO> questionBankquestionVOPage = new Page<>(questionBankquestionPage.getCurrent(), questionBankquestionPage.getSize(), questionBankquestionPage.getTotal());
        if (CollUtil.isEmpty(questionBankquestionList)) {
            return questionBankquestionVOPage;
        }
        // 对象列表 => 封装对象列表
        List<QuestionBankQuestionVO> questionBankquestionVOList = questionBankquestionList.stream().map(questionBankquestion -> {
            return QuestionBankQuestionVO.objToVo(questionBankquestion);
        }).collect(Collectors.toList());

        // region 可选
        // 1. 关联查询用户信息
        Set<Long> userIdSet = questionBankquestionList.stream().map(QuestionBankQuestion::getUserId).collect(Collectors.toSet());
        Map<Long, List<User>> userIdUserListMap = userService.listByIds(userIdSet).stream()
                .collect(Collectors.groupingBy(User::getId));
//        // 2. 已登录，获取用户点赞、收藏状态
//        Map<Long, Boolean> questionBankquestionIdHasThumbMap = new HashMap<>();
//        Map<Long, Boolean> questionBankquestionIdHasFavourMap = new HashMap<>();
//        User loginUser = userService.getLoginUserPermitNull(request);
        // 填充信息
        questionBankquestionVOList.forEach(questionBankquestionVO -> {
            Long userId = questionBankquestionVO.getUserId();
            User user = null;
            if (userIdUserListMap.containsKey(userId)) {
                user = userIdUserListMap.get(userId).get(0);
            }
            questionBankquestionVO.setUser(userService.getUserVO(user));
        });
        // endregion

        questionBankquestionVOPage.setRecords(questionBankquestionVOList);
        return questionBankquestionVOPage;
    }


        @Override
        @Transactional(rollbackFor = Exception.class)
        public void batchAddQuestionsToBank(List<Long> questionIdList, Long questionBankId, User loginUser) {
            // 参数校验
            ThrowUtils.throwIf(CollUtil.isEmpty(questionIdList), ErrorCode.PARAMS_ERROR, "题目列表为空");
            ThrowUtils.throwIf(questionBankId == null || questionBankId <= 0, ErrorCode.PARAMS_ERROR, "题库非法");
            ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN_ERROR);
            // 检查题目 id 是否存在
            LambdaQueryWrapper<Question> questionLambdaQueryWrapper = Wrappers.lambdaQuery(Question.class)
                    .select(Question::getId)
                    .in(Question::getId, questionIdList);
            // 合法的题目 id
            List<Long> validQuestionIdList = questionService.listObjs(questionLambdaQueryWrapper, obj -> (Long) obj);
            ThrowUtils.throwIf(CollUtil.isEmpty(validQuestionIdList), ErrorCode.PARAMS_ERROR, "合法的题目列表为空");
            // 检查题库 id 是否存在
            QuestionBank questionBank = questionBankService.getById(questionBankId);
            ThrowUtils.throwIf(questionBank == null, ErrorCode.NOT_FOUND_ERROR, "题库不存在");
            // 自定义线程池
            ThreadPoolExecutor customExecutor = new ThreadPoolExecutor(
                    20,                         // 核心线程数
                    50,                        // 最大线程数
                    60L,                       // 线程空闲存活时间
                    TimeUnit.SECONDS,           // 存活时间单位
                    new LinkedBlockingQueue<>(10000),  // 阻塞队列容量
                    new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略：由调用线程处理任务
            );

            // 检查哪些题目还不存在于题库中，避免重复插入
            LambdaQueryWrapper<QuestionBankQuestion> lambdaQueryWrapper = Wrappers.lambdaQuery(QuestionBankQuestion.class)
                    .eq(QuestionBankQuestion::getQuestionBankId, questionBankId)
                    .in(QuestionBankQuestion::getQuestionId, validQuestionIdList);
            List<QuestionBankQuestion> existQuestionList = this.list(lambdaQueryWrapper);
            // 已存在于题库中的题目 id
            Set<Long> existQuestionIdSet = existQuestionList.stream()
                    .map(QuestionBankQuestion::getQuestionId)
                    .collect(Collectors.toSet());
            // 已存在于题库中的题目 id，不需要再次添加
            validQuestionIdList = validQuestionIdList.stream().filter(questionId -> {
                return !existQuestionIdSet.contains(questionId);
            }).collect(Collectors.toList());
            ThrowUtils.throwIf(CollUtil.isEmpty(validQuestionIdList), ErrorCode.PARAMS_ERROR, "所有题目都已存在于题库中");

            // 用于保存所有批次的 CompletableFuture
            List<CompletableFuture<Void>> futures = new ArrayList<>();
            // 分批处理避免长事务，假设每次处理 1000 条数据
            int batchSize = 1000;
            int totalQuestionListSize = validQuestionIdList.size();
            for (int i = 0; i < totalQuestionListSize; i += batchSize) {
                // 生成每批次的数据
                List<Long> subList = validQuestionIdList.subList(i, Math.min(i + batchSize, totalQuestionListSize));
                List<QuestionBankQuestion> questionBankQuestions = subList.stream().map(questionId -> {
                    QuestionBankQuestion questionBankQuestion = new QuestionBankQuestion();
                    questionBankQuestion.setQuestionBankId(questionBankId);
                    questionBankQuestion.setQuestionId(questionId);
                    questionBankQuestion.setUserId(loginUser.getId());
                    return questionBankQuestion;
                }).collect(Collectors.toList());
                // 使用事务处理每批数据
                QuestionBankQuestionService questionBankQuestionService = (QuestionBankQuestionServiceImpl) AopContext.currentProxy();
                // 异步处理每批数据并添加到 futures 列表
                CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                    questionBankQuestionService.batchAddQuestionsToBankInner(questionBankQuestions);
                }, customExecutor);
                futures.add(future);
            }

    // 等待所有批次操作完成
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

    // 关闭线程池
            customExecutor.shutdown();
        }
        @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchRemoveQuestionsFromBank(List<Long> questionIdList, Long questionBankId) {
        // 参数校验
        ThrowUtils.throwIf(CollUtil.isEmpty(questionIdList), ErrorCode.PARAMS_ERROR, "题目列表为空");
        ThrowUtils.throwIf(questionBankId == null || questionBankId <= 0, ErrorCode.PARAMS_ERROR, "题库非法");
        // 执行删除关联
        for (Long questionId : questionIdList) {
            // 构造查询
            LambdaQueryWrapper<QuestionBankQuestion> lambdaQueryWrapper = Wrappers.lambdaQuery(QuestionBankQuestion.class)
                    .eq(QuestionBankQuestion::getQuestionId, questionId)
                    .eq(QuestionBankQuestion::getQuestionBankId, questionBankId);
            boolean result = this.remove(lambdaQueryWrapper);
            if (!result) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "从题库移除题目失败");
            }
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchAddQuestionsToBankInner(List<QuestionBankQuestion> questionBankQuestions) {
        try {
            boolean result = this.saveBatch(questionBankQuestions);
            ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "向题库添加题目失败");
        } catch (DataIntegrityViolationException e) {
            log.error("数据库唯一键冲突或违反其他完整性约束, 错误信息: {}", e.getMessage());
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "题目已存在于该题库，无法重复添加");
        } catch (DataAccessException e) {
            log.error("数据库连接问题、事务问题等导致操作失败, 错误信息: {}", e.getMessage());
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "数据库操作失败");
        } catch (Exception e) {
            // 捕获其他异常，做通用处理
            log.error("添加题目到题库时发生未知错误，错误信息: {}", e.getMessage());
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "向题库添加题目失败");
        }
    }

}
